package ide.ccf.ccproject.wizards;

import ide.ccf.ccproject.CCFProject;
import ide.ccf.ccproject.ProjectCreator;
import ide.ccf.ccproject.ProjectPlugin;
import ide.ccf.ccproject.common.Utils;
import ide.ccf.ccproject.managedbuilder.CCFCCBuildWizard;
import ide.ccf.ccproject.managedbuilder.CCFConfigWizardPage;
import ide.ccf.ccproject.managedbuilder.ConfigConstrants;
import ide.ccf.ccproject.managedbuilder.ManagedBuildWizard;
import ide.ccf.ccproject.managedbuilder.MbsWizardHandler;
import ide.ccf.ccproject.managedbuilder.StdBuildWizard;
import ide.ccf.ccproject.preferences.CCFPreferencePage;
import ide.ccf.ccproject.preferences.PreferenceConstants;
import ide.ccf.ccproject.qmake.IQMakeEnvironmentModifier;
import ide.ccf.ccproject.qmake.QMakeRunner;
import ide.ccf.ccproject.templateengine.SwitchConsoleProjectData;
import ide.ccf.ccproject.translate.Message;
import ide.ccf.ccproject.wizards.pages.CCFMainWizardPage;
import ide.ccf.ccproject.wizards.pages.CCProjectWizardPage;
import ide.ccf.ccproject.wizards.pages.MetadataWizardPage;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExecutableExtension;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.WorkspaceModifyDelegatingOperation;
import org.eclipse.ui.dialogs.PreferencesUtil;
import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard;
import org.eclipse.ui.wizards.newresource.BasicNewResourceWizard;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.envvar.EnvironmentVariable;
import org.eclipse.cdt.core.envvar.IEnvironmentVariable;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IIncludeReference;
import org.eclipse.cdt.core.model.ILanguage;
import org.eclipse.cdt.core.model.LanguageManager;
import org.eclipse.cdt.core.settings.model.ICConfigurationDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescription;
import org.eclipse.cdt.core.settings.model.ICProjectDescriptionManager;
import org.eclipse.cdt.core.settings.model.ICResourceDescription;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.newui.MultiCfgContributedEnvironment;
import org.eclipse.cdt.ui.wizards.CWizardHandler;
import org.eclipse.cdt.ui.wizards.IWizardWithMemory;
import org.eclipse.cdt.utils.envvar.IEnvironmentChangeListener;
import org.eclipse.cdt.utils.envvar.StorableEnvironment;
import org.eclipse.cdt.utils.spawner.EnvironmentReader;

import org.eclipse.cdt.internal.core.envvar.EnvironmentVariableManager;
import org.eclipse.cdt.internal.core.envvar.UserDefinedEnvironmentSupplier;
import org.eclipse.cdt.internal.core.model.BinaryParserConfig;
import org.eclipse.cdt.internal.core.model.CModelManager;
import org.eclipse.cdt.internal.ui.actions.WorkbenchRunnableAdapter;
import org.eclipse.cdt.internal.ui.wizards.ICDTCommonProjectWizard;

@SuppressWarnings({ "unused", "restriction" })
public abstract class CCFCommonProjectWizard extends BasicNewResourceWizard
implements IExecutableExtension, IWizardWithMemory, ICDTCommonProjectWizard, ProjectCreatorListener
{
	private static final String PREFIX= "CCProjectWizard"; //$NON-NLS-1$
	private static final String OP_ERROR= "CProjectWizard.op_error"; //$NON-NLS-1$
	private static final String title= CUIPlugin.getResourceString(OP_ERROR + ".title"); //$NON-NLS-1$
	private static final String message= CUIPlugin.getResourceString(OP_ERROR + ".message"); //$NON-NLS-1$
	private static final String[] EMPTY_ARR = new String[0];
	private static final String SEPARATOR = System.getProperty("path.separator", ";"); //$NON-NLS-1$ //$NON-NLS-2$
	ICConfigurationDescription cfg = null;
	private StorableEnvironment vars = null;
	private static final UserDefinedEnvironmentSupplier fUserSupplier = EnvironmentVariableManager.fUserSupplier;

	protected IConfigurationElement fConfigElement;
	protected CCFMainWizardPage fMainPage;
	protected CCFProject cCFProject;

	protected IProject newProject;
	private String wz_title;
	private String wz_desc;

	private boolean existingPath = false;
	private String lastProjectName = null;
	private URI lastProjectLocation = null;
	private CWizardHandler savedHandler = null;
	public static boolean bIsSetPreferences = false;
	@SuppressWarnings("restriction")
	public CCFCommonProjectWizard() {
		this(Message.NewModelProjectWizard_2,Message.NewModelProjectWizard_1);
	}

	public CCFCommonProjectWizard(String title, String desc) {
		super();
		setDialogSettings(CUIPlugin.getDefault().getDialogSettings());
		setNeedsProgressMonitor(true);
		setForcePreviousAndNextButtons(true);
		setWindowTitle(title);
		wz_title = title;
		wz_desc = desc;
	}

	@Override
	public void addPages() {
		fMainPage= new CCFMainWizardPage(CUIPlugin.getResourceString(PREFIX));
		fMainPage.setTitle(wz_title);
		fMainPage.setDescription(wz_desc);
		addPage(fMainPage);
	
	}

	/**
	 * @return true if user has changed settings since project creation
	 */
	private boolean isChanged() {
		if (savedHandler != fMainPage.h_selected)
			return true;

		if (!fMainPage.getProjectName().equals(lastProjectName))
			return true;

		URI projectLocation = fMainPage.getProjectLocation();
		if (projectLocation == null) {
			if (lastProjectLocation != null)
				return true;
		} else if (!projectLocation.equals(lastProjectLocation))
			return true;

		return savedHandler.isChanged();
	}

	public IProject getProject(boolean defaults) {
		return getProject(defaults, true);
	}

	public IProject getProject(boolean defaults, boolean onFinish) {
		if (newProject != null && isChanged())
			clearProject();
		if (newProject == null)	{
            existingPath = false;
		  	try {
		  		IFileStore fs;
				URI p = fMainPage.getProjectLocation();
			  	if (p == null) {
			  		fs = EFS.getStore(ResourcesPlugin.getWorkspace().getRoot().getLocationURI());
				    fs = fs.getChild(fMainPage.getProjectName());
			  	} else
			  		fs = EFS.getStore(p);
		  		IFileInfo f = fs.fetchInfo();
		  		if (f.exists() && f.isDirectory()) {
		  			if (fs.getChild(".project").fetchInfo().exists()) { //$NON-NLS-1$
						if (!MessageDialog.openConfirm(getShell(), Message.CCFCommonProjectWizard_0, Message.CCFCommonProjectWizard_1))
	                		return null;
	                }
	                existingPath = true;
		  		}
        	} catch (CoreException e) {
        		CUIPlugin.log(e.getStatus());
        	}
			savedHandler = fMainPage.h_selected;
			savedHandler.saveState();
			lastProjectName = fMainPage.getProjectName();
			lastProjectLocation = fMainPage.getProjectLocation();
			// start creation process
			invokeRunnable(getRunnable(defaults, onFinish));
		}
		return newProject;
	}

	/**
	 * Remove created project either after error
	 * or if user returned back from config page.
	 */
	private void clearProject() {
		if (lastProjectName == null) return;
		try {
			ResourcesPlugin.getWorkspace().getRoot().getProject(lastProjectName).delete(!existingPath, true, null);
		} catch (CoreException ignore) {}
		newProject = null;
		lastProjectName = null;
		lastProjectLocation = null;
	}

	private boolean invokeRunnable(IRunnableWithProgress runnable) {
		IRunnableWithProgress op= new WorkspaceModifyDelegatingOperation(runnable);
		try {
			getContainer().run(true, true, op);
		} catch (InvocationTargetException e) {
			CUIPlugin.errorDialog(getShell(), title, message, e.getTargetException(), true);
			clearProject();
			return false;
		} catch  (InterruptedException e) {
			clearProject();
			return false;
		}
		return true;
	}

	@SuppressWarnings({ "deprecation", "unused", "restriction" })
	@Override
	public boolean performFinish() {
		boolean needsPost = (newProject != null && !isChanged());
		
		//createNewProject(fMainPage);//TODOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
		
		// create project if it is not created yet
		if (getProject(fMainPage.isCurrent(), true) == null)
			return false;
		
		String defaultVersion = ProjectPlugin.getDefault().getPreferenceStore().getString(PreferenceConstants.CCFVERSION_DEFAULT);
		String binPath = CCFPreferencePage.getCCFVersionBinPath(defaultVersion);
		setBinEnv(defaultVersion, binPath);
		if(binPath == null)
			return false;
		
		fMainPage.h_selected.postProcess(newProject, needsPost);
		try {
			setCreated();
		} catch (CoreException e) {
			e.printStackTrace();
			return false;
		}
		BasicNewProjectResourceWizard.updatePerspective(fConfigElement);
		selectAndReveal(newProject);
		addStoreInfo();
		
		ICResourceDescription cfgd = null;
		ICConfigurationDescription cfgDescription = null;
	
		ICProjectDescription des = CoreModel.getDefault().getProjectDescription(newProject, false);
		if(des != null)
		{
			cfgDescription = des.getDefaultSettingConfiguration();
			IPath projectPath = Platform.getLocation();
			cfgd = cfgDescription.getResourceDescription(projectPath, false);
		}
		//针对CCF增加相应路径到PATH环境变量
		ICConfigurationDescription[] cfgs = null;
		if(cfgDescription != null)
			cfgs = new ICConfigurationDescription[] {cfgDescription};
		if (cfgDescription == null && vars == null)
			vars = fUserSupplier.getWorkspaceEnvironmentCopy();
		if (cfgd == null)
			vars.createVariable("PATH", binPath,
					IEnvironmentVariable.ENVVAR_PREPEND, SEPARATOR);
		else
			for (ICConfigurationDescription cfg : cfgs) {
				MultiCfgContributedEnvironment ce = new MultiCfgContributedEnvironment();
				ce.addVariable("PATH", binPath,
						IEnvironmentVariable.ENVVAR_PREPEND,
						SEPARATOR, cfg);
			}
		
		ManagedBuildWizard.isCCFCatagory = false;
		CCFCCBuildWizard.isCCFCatagory = false;
		StdBuildWizard.isCCFCatagory = false;
		
		//BinaryParserConfig[] binConfigs = CModelManager.getDefault().getBinaryParser(newProject);
		try {
			ICProject[] projects = CCorePlugin.getDefault().getCoreModel().getCModel().getCProjects();
			IIncludeReference[] includeReferences = projects[0].getIncludeReferences();
			IPath projectPath = projects[0].getPath();
			String out = projectPath.toOSString();
			System.out.println("out:" + out);
		} catch (CModelException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
		IWorkspaceRunnable op = new IWorkspaceRunnable() 
		{
			public void run(IProgressMonitor monitor) throws CoreException, OperationCanceledException 
			{
				MetadataWizardPage.createFile(monitor, newProject);
			}
		};
		
		try 
		{
			getContainer().run(false, false, new WorkbenchRunnableAdapter(op));
		} 
		catch (Exception e) 
		{
			return false;
		}	

		return true;
	}
	
	
	private void addStoreInfo() {
		String out = MetadataWizardPage.getAutorities();
		IPreferenceStore store = ProjectPlugin.getDefault().getPreferenceStore();
		store.setValue(PreferenceConstants.CCFRUNTIME_VERSION, CCFMainWizardPage.c_runtime.getText().trim() == null ? "" : CCFMainWizardPage.c_runtime.getText().trim());
		store.setValue(PreferenceConstants.CCFSDK_VERSION, CCFMainWizardPage.c_sdk.getText().trim() == null ? "" : CCFMainWizardPage.c_sdk.getText().trim());
		store.setValue(ConfigConstrants.CONTEXTALL, out == null ? "" : out);
	}

	@SuppressWarnings("restriction")
	private void setBinEnv(String defaultVersion, String binPath) {
		// $ADD-CCF-MENCIUS-1$
		if (binPath == null) 
		{
			if(QMakeRunner.bIsSetPreferences == false)
			{
				PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() 
				{
					public void run() 
					{
						Shell shell = PlatformUI.getWorkbench().getDisplay().getActiveShell();
						MessageDialog dialog = new MessageDialog(shell, "构建错误", null, "未设置默认CCF版本。打开首选项对话框中的CCF页面并添加CCF版本。", MessageDialog.ERROR, new String[] { "打开首选项", IDialogConstants.CANCEL_LABEL }, 0);
						bIsSetPreferences = true;
						if (dialog.open() == 1)
							return;
						PreferencesUtil.createPreferenceDialogOn(shell, "ide.ccf.ccproject.preferences.CCFPreferencePage", null, null).open();
					}
				});
			}
			QMakeRunner.bIsSetPreferences = false;

			if (newProject != null) 
			{
				cCFProject = new CCFProject(newProject);
				binPath = cCFProject.getBinPath();
			} 
			else 
			{
				defaultVersion = ProjectPlugin.getDefault().getPreferenceStore().getString(PreferenceConstants.CCFVERSION_DEFAULT);
				binPath = CCFPreferencePage.getCCFVersionBinPath(defaultVersion);
			}
		}
		
		if(binPath == null)
			return;
	}

	/**
	 * 创建新项目
	 */
	@SuppressWarnings("unused")
	private void createNewProject(CCFMainWizardPage fMainPage) {
		ProjectCreator procreator = new ProjectCreator(fMainPage.getProjectHandle(), fMainPage.getLocationPath());
		procreator.addProjectCreatorListener(this);
		procreator.create(getContainer());
		
	}

	protected boolean setCreated() throws CoreException {
		ICProjectDescriptionManager mngr = CoreModel.getDefault().getProjectDescriptionManager();

		ICProjectDescription des = mngr.getProjectDescription(newProject, false);

		if(des == null ) {
			return false;
		}

		if(des.isCdtProjectCreating()){
			des = mngr.getProjectDescription(newProject, true);
			des.setCdtProjectCreated();
			mngr.setProjectDescription(newProject, des, false, null);
			return true;
		}
		return false;
	}

    @Override
	public boolean performCancel() {
    	clearProject();
        return true;
    }

	public void setInitializationData(IConfigurationElement config, String propertyName, Object data) throws CoreException {
		fConfigElement= config;
	}

	private IRunnableWithProgress getRunnable(boolean _defaults, final boolean onFinish) {
		final boolean defaults = _defaults;
		return new IRunnableWithProgress() {
			public void run(IProgressMonitor imonitor) throws InvocationTargetException, InterruptedException {
				final Exception except[] = new Exception[1];
				getShell().getDisplay().syncExec(new Runnable() {
					public void run() {
						IRunnableWithProgress op= new WorkspaceModifyDelegatingOperation(new IRunnableWithProgress() {
							@SuppressWarnings("restriction")
							public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
								final IProgressMonitor fMonitor;
								if (monitor == null) {
									fMonitor= new NullProgressMonitor();
								} else {
									fMonitor = monitor;
								}
								fMonitor.beginTask(/*CUIPlugin.getResourceString("CCF_C++ Project Wizard")*/"CCF_C++ Project Wizard", 100); //$NON-NLS-1$
								fMonitor.worked(10);
								try {
									newProject = createIProject(lastProjectName, lastProjectLocation, new SubProgressMonitor(fMonitor, 40));
									if (newProject != null)
										fMainPage.h_selected.createProject(newProject, defaults, onFinish, new SubProgressMonitor(fMonitor, 40));
									fMonitor.worked(10);
								} catch (CoreException e) {	CUIPlugin.log(e); }
								finally {	
									fMonitor.done();
								}
							}
						});
						try {
							getContainer().run(false, true, op);
						} catch (InvocationTargetException e) {
							except[0] = e;
						} catch (InterruptedException e) {
							except[0] = e;
						}
					}
				});
				if (except[0] != null) {
					if (except[0] instanceof InvocationTargetException) {
						throw (InvocationTargetException)except[0];
					}
					if (except[0] instanceof InterruptedException) {
						throw (InterruptedException)except[0];
					}
					throw new InvocationTargetException(except[0]);
				}
			}
		};
	}

	public IProject createIProject(final String name, final URI location) throws CoreException{
		return createIProject(name, location, new NullProgressMonitor());
	}

	/**
	 * @since 5.1
	 */
	protected IProgressMonitor continueCreationMonitor;

	/**
	 * @param monitor
	 * @since 5.1
	 *
	 */
	public IProject createIProject(final String name, final URI location, IProgressMonitor monitor) throws CoreException{

		monitor.beginTask(Message.CCFCommonProjectWizard_creatingProject, 100);

		if (newProject != null)	return newProject;

		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();
		final IProject newProjectHandle = root.getProject(name);

		if (!newProjectHandle.exists()) {
//			IWorkspaceDescription workspaceDesc = workspace.getDescription();
//			workspaceDesc.setAutoBuilding(false);
//			workspace.setDescription(workspaceDesc);
			IProjectDescription description = workspace.newProjectDescription(newProjectHandle.getName());
			if(location != null)
				description.setLocationURI(location);
			newProject = CCorePlugin.getDefault().createCDTProject(description, newProjectHandle, new SubProgressMonitor(monitor,25));
		} else {
			IWorkspaceRunnable runnable = new IWorkspaceRunnable() {
				public void run(IProgressMonitor monitor) throws CoreException {
					newProjectHandle.refreshLocal(IResource.DEPTH_INFINITE, monitor); 
				}
			};
			workspace.run(runnable, root, IWorkspace.AVOID_UPDATE, new SubProgressMonitor(monitor,25));
			newProject = newProjectHandle;
		}

		// Open the project if we have to
		if (!newProject.isOpen()) {
			newProject.open(new SubProgressMonitor(monitor,25));
		}

		continueCreationMonitor = new SubProgressMonitor(monitor,25);
		newProject.refreshLocal(IResource.DEPTH_INFINITE, monitor);
		IProject proj = continueCreation(newProject);
		monitor.done();

		return proj;
	}

	protected abstract IProject continueCreation(IProject prj);
	public abstract String[] getNatures();

	@Override
	public void dispose() {
		fMainPage.dispose();
	}

    @Override
	public boolean canFinish() {
    	if (fMainPage.h_selected != null) {
    		if(!fMainPage.h_selected.canFinish())
    			return false;
    		String s = fMainPage.h_selected.getErrorMessage();
    		if (s != null) return false;
    	}
    	return super.canFinish();
    }
    /**
     * Returns last project name used for creation
     */
	public String getLastProjectName() {
		return lastProjectName;
	}

	public URI getLastProjectLocation() {
		return lastProjectLocation;
	}

	public IProject getLastProject() {
		return newProject;
	}

	// Methods below should provide data for language check
	public String[] getLanguageIDs (){
		String[] contentTypeIds = getContentTypeIDs();
		if(contentTypeIds.length > 0) {
			IContentTypeManager manager = Platform.getContentTypeManager();
			List<String> languageIDs = new ArrayList<String>();
			for(int i = 0; i < contentTypeIds.length; ++i) {
				IContentType contentType = manager.getContentType(contentTypeIds[i]);
				if(null != contentType) {
					ILanguage language = LanguageManager.getInstance().getLanguage(contentType);
					if(!languageIDs.contains(language.getId())) {
						languageIDs.add(language.getId());
					}
				}
			}
			return languageIDs.toArray(new String[languageIDs.size()]);
		}
		return EMPTY_ARR;
	}
	public String[] getContentTypeIDs (){
		return EMPTY_ARR;
	}
	public String[] getExtensions (){
		String[] contentTypeIds = getContentTypeIDs();
		if(contentTypeIds.length > 0) {
			IContentTypeManager manager = Platform.getContentTypeManager();
			List<String> extensions = new ArrayList<String>();
			for(int i = 0; i < contentTypeIds.length; ++i) {
				IContentType contentType = manager.getContentType(contentTypeIds[i]);
				if(null != contentType) {
					String[] thisTypeExtensions = contentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
					extensions.addAll(Arrays.asList(thisTypeExtensions));
				}
			}
			return extensions.toArray(new String[extensions.size()]);
		}
		return EMPTY_ARR;
	}

}